package com.xinongbest.oa.service;

import com.xinongbest.oa.dao.EmployeeDao;
import com.xinongbest.oa.dao.LeaveFormDao;
import com.xinongbest.oa.dao.NoticeDao;
import com.xinongbest.oa.dao.ProcessFlowDao;
import com.xinongbest.oa.entity.Employee;
import com.xinongbest.oa.entity.LeaveForm;
import com.xinongbest.oa.entity.Notice;
import com.xinongbest.oa.entity.ProcessFlow;
import com.xinongbest.oa.service.exception.BusinessException;
import com.xinongbest.oa.utils.MybatisUtils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author lemonwan
 */
public class LeaveFormService {
    /**
     * 创建请假申请单
     *
     * @param form
     * @return
     */
    public LeaveForm createLeaveForm(LeaveForm form) {
        /** 请假流程中所有的操作需要放在事务中执行 */
        LeaveForm completeForm = (LeaveForm) MybatisUtils.executeUpdate(sqlSession -> {
            // 请假表单是前台提交的，但是不部分表单的数据是需要修改的
            // 如果是8级以下员工的申请，状态是处理中；8级以上则直接为审批通过
            EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
            Employee employee = employeeDao.selectById(form.getEmployeeId());
            if (employee.getLevel() == 8) {
                form.setState("approved");
            } else {
                form.setState("processing");
            }
            // 插入到请假单表
            LeaveFormDao leaveFormDao = sqlSession.getMapper(LeaveFormDao.class);
            leaveFormDao.insert(form);
            // 生成流程数据
            ProcessFlow processFlow = new ProcessFlow();
            processFlow.setFormId(form.getFormId());
            processFlow.setOperatorId(form.getEmployeeId());
            processFlow.setAction("apply");
            processFlow.setCreateTime(new Date());
            processFlow.setOrderNo(1);
            processFlow.setState("complete");
            processFlow.setIsLast(0);
            ProcessFlowDao processFlowDao = sqlSession.getMapper(ProcessFlowDao.class);
            processFlowDao.insert(processFlow);
            // 生成请假任务的任务的时候，还需要产生一条通知数据
            NoticeDao noticeDao = sqlSession.getMapper(NoticeDao.class);
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH时");
            // 7级以下员工，生成经理审批任务；7级以上员工生成总经理任务
            if (employee.getLevel() < 7) {
                ProcessFlow flow2 = new ProcessFlow();
                // 获取上级领导
                Employee depLeader = employeeDao.selectLeader(employee);
                flow2.setFormId(form.getFormId());
                flow2.setOperatorId(depLeader.getEmployeeId());
                flow2.setAction("audit");
                flow2.setCreateTime(new Date());
                flow2.setOrderNo(2);
                flow2.setState("process");
                Long diff = form.getEndTime().getTime() - form.getStartTime().getTime();
                float diffHours = diff / (1000 * 60 * 60) * 1L;
                if (diffHours > BusinessConstants.MANAGER_AUDIT_HOURS) {
                    flow2.setIsLast(0);
                    processFlowDao.insert(flow2);
                    // 如果请假时间大于36小时，则需要生成总经理审批任务
                    ProcessFlow flow3 = new ProcessFlow();
                    Employee leader = employeeDao.selectLeader(depLeader);
                    flow3.setFormId(form.getFormId());
                    flow3.setOperatorId(leader.getEmployeeId());
                    flow3.setAction("audit");
                    flow3.setCreateTime(new Date());
                    flow3.setOrderNo(3);
                    flow3.setState("ready");
                    flow3.setIsLast(1);
                    processFlowDao.insert(flow3);
                } else {
                    flow2.setIsLast(1);
                    processFlowDao.insert(flow2);
                }
                // 产生员工请假通知数据
                String content = String.format("您的请假申请[%s-%s]申请已提交，请等待上级审批。", dateFormat.format(form.getStartTime()), dateFormat.format(form.getEndTime()));
                noticeDao.insert(new Notice(form.getEmployeeId(), content));
                // 产生领导审批通知数据
                content = String.format("%s-%s提起的请假申请[%s-%s]，请尽快审批", employee.getTitle(), employee.getName(), dateFormat.format(form.getStartTime()), dateFormat.format(form.getEndTime()));
                noticeDao.insert(new Notice(depLeader.getEmployeeId(), content));
            } else if (employee.getLevel() < 7) {
                // 经理请假申请，只需要总经理审批
                Employee leader = employeeDao.selectLeader(employee);
                ProcessFlow flow = new ProcessFlow();
                flow.setFormId(form.getFormId());
                flow.setOperatorId(leader.getEmployeeId());
                flow.setAction("audit");
                flow.setCreateTime(new Date());
                flow.setOrderNo(2);
                flow.setState("process");
                flow.setIsLast(1);
                processFlowDao.insert(flow);
                // 产生员工请假通知数据
                String content = String.format("您的请假申请[%s-%s]申请已提交，请等待上级审批。", dateFormat.format(form.getStartTime()), dateFormat.format(form.getEndTime()));
                noticeDao.insert(new Notice(form.getEmployeeId(), content));
                // 产生领导审批通知数据
                content = String.format("%s-%s提起的请假申请[%s-%s]，请尽快审批", employee.getTitle(), employee.getName(), dateFormat.format(form.getStartTime()), dateFormat.format(form.getEndTime()));
                noticeDao.insert(new Notice(leader.getEmployeeId(), content));
            } else {
                // 总经理请假申请
                ProcessFlow flow = new ProcessFlow();
                flow.setFormId(form.getFormId());
                flow.setOperatorId(employee.getEmployeeId());
                flow.setAction("audit");
                flow.setResult("approved");
                flow.setReason("自动通过");
                flow.setCreateTime(new Date());
                flow.setAuditTime(new Date());
                flow.setOrderNo(2);
                flow.setState("complete");
                flow.setIsLast(1);
                processFlowDao.insert(flow);
                // 产生员工请假通知数据
                String content = String.format("您的请假申请[%s-%s]系统已自动审批通过。", dateFormat.format(form.getStartTime()), dateFormat.format(form.getEndTime()));
                noticeDao.insert(new Notice(form.getEmployeeId(), content));
            }
            return form;
        });
        return completeForm;
    }

    /**
     * <h2>依据状态查询待审批任务</h2>
     *
     * @param state
     * @param operatorId
     * @return
     */
    public List<Map<String, Object>> getLeaveFormList(String state, Long operatorId) {
        return (List<Map<String, Object>>) MybatisUtils.executeQuery(sqlSession -> {
            LeaveFormDao leaveFormDao = sqlSession.getMapper(LeaveFormDao.class);
            return leaveFormDao.selectByParam(state, operatorId);
        });
    }

    /**
     * <h2>审批任务</h2>
     *
     * @param formId
     * @param operatorId
     * @param result
     * @param reason
     */
    public void audit(Long formId, Long operatorId, String result, String reason) {
        // 不管审核结果是通过还是拒绝，则当前操作的状态变为process
        MybatisUtils.executeUpdate(sqlSession -> {
            ProcessFlowDao processFlowDao = sqlSession.getMapper(ProcessFlowDao.class);
            List<ProcessFlow> processFlowList = processFlowDao.selectByParam(formId);
            if (processFlowList.size() == 0) {
                throw new BusinessException("PF001", "无效的审批流程");
            }
            List<ProcessFlow> flowList = processFlowList.stream().filter(p -> p.getOperatorId() == operatorId && p.getState().equals("process")).collect(Collectors.toList());
            ProcessFlow processFlow = null;
            Date auditTime = new Date();
            if (flowList.size() == 0) {
                throw new BusinessException("PF002", "未找到待处理的任务");
            } else {
                processFlow = flowList.get(0);
                processFlow.setState("complete");
                processFlow.setReason(reason);
                processFlow.setResult(result);
                processFlow.setAuditTime(auditTime);
                processFlowDao.update(processFlow);
            }
            LeaveFormDao leaveFormDao = sqlSession.getMapper(LeaveFormDao.class);
            LeaveForm form = leaveFormDao.selectById(formId);
            // 通知逻辑处理
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH时");
            EmployeeDao employeeDao = sqlSession.getMapper(EmployeeDao.class);
            Employee employee = employeeDao.selectById(form.getEmployeeId()); // 表单提交人信息
            Employee operator = employeeDao.selectById(operatorId); // 审批任务经办人信息
            NoticeDao noticeDao = sqlSession.getMapper(NoticeDao.class);
            // 如果当前任务是最后一个节点，代表流程结束，则更新申请单的状态为对应的approved或者refused
            if (processFlow.getIsLast() == 1) {
                form.setState(result);
                leaveFormDao.update(form);
                // 如果审批流程走到最后一步，需要发起通知
                String strResult = null;
                if (result.equals("approved")) {
                    strResult = "批准";
                } else if (result.equals("refused")) {
                    strResult = "驳回";
                }
                // 表单提交人通知
                String noticeContent = String.format("您的请假申请[%s-%s]%s%s已%s，审批意见：%s，审批流程已结束",
                        dateFormat.format(form.getStartTime()), dateFormat.format(form.getEndTime()),
                        operator.getTitle(), operator.getName(), strResult, reason);
                noticeDao.insert(new Notice(form.getEmployeeId(), noticeContent));
                // 审批人信息
                noticeContent = String.format("%s-%s提起的请假申请[%s-%s]您已%s，审批意见：%s，审批时间：%s，审批流程已结束",
                        employee.getTitle(), employee.getName(),
                        dateFormat.format(form.getStartTime()), dateFormat.format(form.getEndTime()),
                        strResult, reason, dateFormat.format(auditTime));
                noticeDao.insert(new Notice(operator.getEmployeeId(), noticeContent));
            } else {
                List<ProcessFlow> ready = processFlowList.stream().filter(p -> p.getState().equals("ready")).collect(Collectors.toList());
                if (result.equals("approved")) {
                    // 如果当前任务不是最后一个节点且审批通过，则下一个任务节点的状态从ready变更为process
                    ProcessFlow flow = ready.get(0);
                    flow.setState("process");
                    processFlowDao.update(flow);
                    // 消息一：表单提交人，部门经理已经审批，提交总经理审批
                    String noticeContent = String.format("您的请假申请[%s-%s]%s%s已批准，审批意见：%s",
                            dateFormat.format(form.getStartTime()), dateFormat.format(form.getEndTime()),
                            employee.getTitle(), employee.getName(), reason);
                    noticeDao.insert(new Notice(form.getEmployeeId(), noticeContent));
                    // 消息二：通知总经理有新的审批任务
                    noticeContent = String.format("%s-%s提起的请假申请[%s-%s]，请尽快审批", employee.getTitle(), employee.getName(),
                            dateFormat.format(form.getStartTime()), dateFormat.format(form.getEndTime()));
                    noticeDao.insert(new Notice(flow.getOperatorId(), noticeContent));
                    // 消息三：通知部门经理（当前经办人），员工的请假单已经审批，交由上级继续审批
                    noticeContent = String.format("%s-%s提起的请假申请[%s-%s]，您已批准，审批意见：%s，申请转至上级领导继续审批",
                            employee.getTitle(), employee.getName(),
                            dateFormat.format(form.getStartTime()), dateFormat.format(form.getEndTime()), reason);
                    noticeDao.insert(new Notice(operator.getEmployeeId(), noticeContent));
                } else if (result.equals("refused")) {
                    // 如果当前任务不是最后一个节点且审批不通过，则下一个任务节点的状态从ready变更为cancel，且请假当状态变更为refused
                    for (ProcessFlow flow : ready) {
                        flow.setState("cancel");
                        processFlowDao.update(flow);
                    }
                    form.setState("refused");
                    leaveFormDao.update(form);
                    // 通知申请人表单已被驳回
                    String noticeContent = String.format("您的请假申请[%s-%s]%s%s已驳回，驳回人：%s-%s，驳回理由：%s",
                            dateFormat.format(form.getStartTime()), dateFormat.format(form.getEndTime()),
                            employee.getTitle(), employee.getName(), operator.getTitle(), operator.getName(), reason);
                    noticeDao.insert(new Notice(form.getEmployeeId(), noticeContent));
                    // 通知经办人表单"您已驳回"
                    noticeContent = String.format("%s-%s提起的请假申请[%s-%s]，您已驳回", employee.getTitle(), employee.getName(),
                            dateFormat.format(form.getStartTime()), dateFormat.format(form.getEndTime()));
                    noticeDao.insert(new Notice(operator.getEmployeeId(), noticeContent));
                }
            }
            return null;
        });
    }
}
